package xyz.lwm.lazycat.utility.reflect;

import xyz.lwm.lazycat.utility.CollectionUtil;
import xyz.lwm.lazycat.utility.It;
import xyz.lwm.lazycat.utility.StringUtil;
import xyz.lwm.lazycat.utility.Trio;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 处理 jdk 注解的属性
 *
 * @author lwm
 */
class AliasHandler {

    // 当前注解
    private final Annotation annotation;

    // 注解集
    private final Annotations annotations;

    // left: attribute name; middle: alias to name; right: annotation class
    private final List<Trio<String, String, Class<? extends Annotation>>> aliases;

    // key: attribute name; value: attribute value
    private final Map<String, Object> attributes;

    AliasHandler(Annotation annotation, Annotations annotations) {
        this.annotation = annotation;
        this.annotations = annotations;
        this.aliases = new ArrayList<>();
        this.attributes = new HashMap<>();
        init();
    }

    private void init() {
        Arrays.stream(annotation.annotationType().getMethods())
                .filter(m -> !m.isSynthetic() &&
                        !ReflectUtil.isObjectMethod(m) )
                .forEach(m -> {
                    // 处理 @AliasTo 注解
                    AliasTo alias = m.getAnnotation(AliasTo.class);
                    if (alias != null) {
                        String toName = alias.value();
                        Class<? extends Annotation> toAnnoClz = alias.annotation();
                        // 无需映射到元注解, 且映射属性名称不为空时, 同时名称不同时, 才添加映射关系
                        if (toAnnoClz == Annotation.class &&
                                StringUtil.isNotBlank(toName) &&
                                !m.getName().equals(toName)) {
                            aliases.add(Trio.of(m.getName(), toName, null));
                        }
                        // 须映射到元注解, 要判断元注解是否存在
                        if (toAnnoClz != Annotation.class && !AnnotationUtil.isMetaAnnotation(toAnnoClz)) {
                            Annotations anns = AnnotationUtil.getAnnotations(annotation.annotationType());
                            // 元注解存在, 才添加映射关系
                            if (anns.hasAnnotation(toAnnoClz)) {
                                toName = StringUtil.isBlank(toName) ? m.getName() : toName;
                                aliases.add(Trio.of(m.getName(), toName, toAnnoClz));
                            }
                        }
                    }
                    // 收集注解的属性值
                    attributes.put(m.getName(), ReflectUtil.getValue(annotation, m));
                });
    }


    public void handle() {
        if (CollectionUtil.isNotEmpty(aliases)) {
            for (Trio<String, String, Class<? extends Annotation>> alias : aliases) {
                String attrName = alias.getLeft();
                String asName = alias.getMiddle();
                Class<? extends Annotation> asClz = alias.getRight();
                if (asClz == null) {
                    // alias to annotation 为空, 则当前注解内部映射赋值
                    // 当前属性有值, 且 alias to 属性无值时, 将当前属性赋值给映射属性;
                    // 如果当前属性无值, 则不用处理;
                    // 如果当前属性和映射属性均有值, 但值不同, 则不处理, 这属于人为错误.
                    Object val = attributes.get(attrName);
                    Object asVal = attributes.get(asName);
                    // 当前属性有值, 映射属性为空值时, 则将当前属性的值赋值给映射属性
                    if (!isEmptyVal(val) && isEmptyVal(asVal)) {
                        attributes.put(asName, val);
                    }
                } else {
                    // alias to annotation 存在, 则代表: 当前注解的属性映射到元注解的属性上
                    // 映射到元注解
                    Annotation asAnno = annotations.getAnnotation(asClz);
                    Map<String, Object> asAttrs = new HashMap<>();
                    asAttrs.put(asName, attributes.get(attrName));
                    overwrite(asAnno, asAttrs);
                }
            }

            // 覆盖注解的属性值
            overwrite(annotation, attributes);
        }

    }

    @SuppressWarnings("unchecked")
    private void overwrite(Annotation annotation, Map<String, Object> attributes) {
        It.of(annotation).filter(a -> Proxy.isProxyClass(a.getClass()))
                .let(Proxy::getInvocationHandler)
                .let(h -> ReflectUtil.getValue(h, "memberValues"))
                .let(Map.class::cast).also(map -> map.putAll(attributes));
    }

    /**
     * 判断属性是否为空值, 包括空字符串, 空数组, null
     *
     * @param val value
     * @return true if empty value
     */
    private boolean isEmptyVal(Object val) {
        if (val == null) {
            return true;
        }
        if (val instanceof CharSequence) {
            return StringUtil.isBlank((CharSequence) val);
        }
        return val.getClass().isArray() && Array.getLength(val) == 0;
    }

}
